package pl.wroc.uni.ii.evolution.distribution.workers;

import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.security.AllPermission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.CodeSource;
import java.util.jar.Attributes;


/**
 * It loads EvolutionaryTask from JAR.
 * Manifest of this JAR must have attribute: IslandCreator.
 * Value of this attribute must be a name of class implementing IslandCreator interface.
 * 
 * @author Piotr Lipinski, Marcin Golebiowski, Kamil Dworakowski
 */
@SuppressWarnings("unchecked")
public class EvTaskLoaderImpl implements EvTaskLoader {

  /** class used for hide reflection interaction */  
  class TaskReflectionWrapper implements Runnable {
    private Object task;
    
    public TaskReflectionWrapper(Object task) {
      this.task = task;
    }
    
    public void run() {
      Class task_class = task.getClass();
      
      Method method = null;
      try {
        method = task_class.getMethod("run", (Class[])null);
        method.invoke(task, (Object[])null);
      } catch (Exception e) {
        EvConsole.println(e.toString());
        e.printStackTrace(System.out);
      }
    } 
  }
  
  private String download_servlet;
  private String upload_servlet;
  private String managment_servlet;
  
  
  
  public EvTaskLoaderImpl(String download_servlet, String upload_servlet, String managment_servlet) {
    this.managment_servlet = managment_servlet;
    this.download_servlet = download_servlet;
    this.upload_servlet = upload_servlet;
  }

  /**
   * Returns instance of EvIsland that is created by 
   * some class in JAR file. Name of this class is stored in JAR's manifest.
   * @param jar_url
   * @param task_id
   * @param node_id
   * @return EvolutionaryTask
   */
  public Runnable getTask(String jar_url, long task_id, long node_id) {

    try {
      EvConsole.println("Loading EvTask from:" + jar_url);
      /** read name of TaskCreator that is stored in JAR manifest */
      URL url = new URL("jar:" + jar_url + "!/");
      JarURLConnection uc = (JarURLConnection) url.openConnection();
      Attributes attr = uc.getMainAttributes();
      String creator_name = attr.getValue("TaskCreator");
      EvConsole.println("IslandCreator: <" + creator_name + ">");
      EvConsole.println("jar_url:<" + jar_url + ">");

      AllPermissionsClassLoader loader = new AllPermissionsClassLoader(
          new URL[] { new URL(jar_url) });

      /** load class from JAR */
      Class creator = loader.loadClass(creator_name);
      EvConsole.println("Class loaded");
      /** create new instance of that class */
      Object obj = creator.newInstance();
      EvConsole.println("Instance created");
      /** get 'create' method */
      Method m = creator.getMethod("create", new Class[] { long.class,
          long.class, String.class, String.class, String.class });
      m.setAccessible(true);
      EvConsole.println("Create method extracted");
      
      /** create EvolutionaryTask */
      Object obj_task = m.invoke(obj, new Object[] { task_id, node_id,
          download_servlet, upload_servlet, managment_servlet });
      EvConsole.println("Task created");
      Runnable task = new TaskReflectionWrapper(obj_task);
      EvConsole.println("Wrapped task created");
      return task;

    } catch (Exception ex) {
      EvConsole.println("LOAD TASK EXCEPTION: " + ex.toString());
      ex.printStackTrace(System.out);
      return null;
    }

  }
}



class AllPermissionsClassLoader extends URLClassLoader {

  public AllPermissionsClassLoader(URL[] urls) {
    super(urls, null);
  }

  public AllPermissionsClassLoader(URL[] urls, ClassLoader parent) {
    super(urls, parent);
  }

  public AllPermissionsClassLoader(URL[] urls, ClassLoader parent,
      URLStreamHandlerFactory factory) {
    super(urls, parent, factory);
  }

  @Override
  protected PermissionCollection getPermissions(CodeSource codesource) {
    Permissions permissions = new Permissions();
    permissions.add(new AllPermission());
    return permissions;
  }
}